//
//  String.cpp
//  Prism
//
//  Created by Bas du Pre on 20-05-11.
//  Copyright 2011 UU. All rights reserved.
//

#include "global.h"
#include "PString.h"

using namespace Prism;

String::String(): szBuffer(NULL), nBuffSize(10) {
	allocate_memory(nBuffSize);
    copy(szBuffer, "");
}

String::String(const char *str): szBuffer(NULL), nBuffSize(getlen(str) + 1) {
	allocate_memory(nBuffSize);
	copy(szBuffer, str);
}

String::String(const String &str): szBuffer(NULL), nBuffSize(str.length() + 1) {
	allocate_memory(nBuffSize);
	copy(szBuffer, str.c_str());
}

String::String(const PFloat &flt): szBuffer(NULL), nBuffSize(32) {
	allocate_memory(nBuffSize);
    sprintf(szBuffer, "%.4g", flt); 
}

String::String(PInt n): szBuffer(NULL), nBuffSize(n) {
	allocate_memory(nBuffSize);
    copy(szBuffer, "");
}

String::String(char c): szBuffer(NULL), nBuffSize(2) {
	allocate_memory(nBuffSize);
	szBuffer[0] = c;
	szBuffer[1] = 0;
}

String::~String() {
	free_memory();
}

// reserves memory for storing a string
// this operation doesn't preserve the 
// characers of the previous string
void String::reserve(PInt n){
	nBuffSize = n;
	if(szBuffer != NULL) {
		this->~String();
	}
	allocate_memory(n);		
}

// reallocates the size of the memory
// used for storing the current string
// this operation preserves all characers
// of the previous string
void String::resize(PInt n) {
	String buffer = *this;
	(*this).reserve(n);
	copy(szBuffer, buffer.c_str());
}

// grows the size of memory
// that is used for storing
// the current string
void String::grow() {
	resize(2*nBuffSize);
}

// erases all characters
// from the current string
void String::clear() {
	szBuffer = (char*)"";
	nBuffSize = 1;
}

String String::operator +(const String &rhs) {
	String temp(szBuffer);
	temp += rhs;
	return temp;
}

String String::operator -(const String &rhs) {
	String temp(szBuffer);
	temp -= rhs;
	return temp;
}

// assigns a string to another
String String::operator =(const String &rhs) {
	(*this).reserve(rhs.length() + 1);
	copy(szBuffer, rhs.c_str());
	return *this;
}

// appends a new string to the current string
// this function also makes sure that there
// is enough space available to store the
// result of the concanetation
String String::operator +=(const String &rhs) {
	PInt len1 = (*this).length();
	PInt len2 = rhs.length();
    
	// this part resizes automaticaly the sizes
	// of the memory allocated for storing the
	// resulting string
	while((len1 + len2 + 1) > (*this).capacity()) {
		grow();
	}
	concat(szBuffer, rhs.c_str());
	return *this;
}

// removes a substring from the current string
String String::operator -=(const String &str) {
	erase(str);
	return *this;
}

// returns a character of the current 
// string in a specific position
char &String::at(PInt n) const {
	char *cp = szBuffer;
	for(int pos = 0; *cp != 0 && pos < n; ++cp, ++pos)
        ;
	return *cp;
}

// returns a substring of the current string
String String::substr(PInt start, PInt end) const {
	String buffer(szBuffer), temp = "";
	for(PInt i = start; i < end; ++i) {
		temp += buffer.at(i);
	}
	return temp;
}

String String::substr(PInt start) const {
	return substr(start, length());
}

// inserts a new substring in the current string
void String::insert(const String &str, PInt pos) {
	String temp = (*this).substr(0, pos) + str;
	temp += (*this).substr(pos + str.length(), (*this).length());
	*this = temp;
}

// erases a substring in a specific position 
// from the current string
void String::erase(const String &str, PInt pos) {
	String temp = (*this).substr(0, pos);
	temp += (*this).substr(pos + str.length(), (*this).length());
	*this = temp;
}

// erases a substring from the current string
bool String::erase(const String &str) {
	int pos = (*this).find(str);
	if(pos != -1) {
		String temp = (*this).substr(0, pos);
		temp += (*this).substr(pos + str.length(), (*this).length());
		*this = temp;
	}
	return pos != -1;
}

// replace one substring by another one from the current string
bool String::replace(const String &str, const String &str2) {
	int pos = (*this).find(str);
	if(pos != -1) {
		String temp = (*this).substr(0, pos) + str2;
		temp += (*this).substr(pos + str.length(), (*this).length());
		*this = temp;
	}
	return pos != -1;
}

// reverses the current string
void String::reverse() {
	PInt len = (*this).length();
	for(int i = 0, j = len - 1; i < j; ++i, --j) {
		std::swap((*this)[i], (*this)[j]);
	}
}

// trims character from the  
// left side of a string
void String::ltrim(char c) {
	PInt len = (*this).length();
    int i = 0;
	for(i = 0; i < len && (*this)[i] == c; ++i) 
		;
	*this = substr(i, len);
}

// trims character from the
// right side of a string
void String::rtrim(char c) {
	for(int i = (*this).length() - 1; i > 0 && (*this)[i] == c; --i) {
		szBuffer[i] = 0;
	}
}

// trims all characters from left and right
// of the current string
void String::trim(const String &delim) {
	PInt len = delim.length();
	for(int i = 0; i < len; ++i) {
		ltrim(delim[i]);
		rtrim(delim[i]);
	}
}

// removes unwanted delimiters from a string
void String::strip(const String &delim) {
	String buffer = "";
	PInt len = (*this).length();
	char prevChar;
	for(int i = 0; i < len; ++i) {
		if((*this)[i] == ' ' && prevChar != ' ') {
			buffer += (*this)[i];
		} else if(!ispunc((*this)[i], delim)) {
			buffer += (*this)[i];
		}
		prevChar = (*this)[i];
	}
	*this = buffer;
}

// verrifies if the char "c" is part of the set of
// delimiters "delim"
bool String::ispunc(char c, const String &delim) {
	return delim.find(c) != -1;
}

// returns the length of a string
int String::getlen(const char *str) const {
	PInt len = 0;
	const char *buffPtr = str;
	while(*buffPtr++) {
		++len;
	}
	return len;
}

// copies the string "src" inside the string "dest"
void String::copy(char *dest, const char *src) {
	char *destPtr = dest;
	while(*src != 0) {
		*destPtr++ = *src++;
	}
	*destPtr = 0;
}

// makes the concanatation of twwo strings
void String::concat(char *str1, const char *str2) {
	char *destPtr = str1;
	while(*destPtr != 0) {
		++destPtr;
	}
	while(*str2 != 0) {
		*destPtr++ = *str2++;
	}
	*destPtr = 0;
}

// makes an alphanumeric comparaison of two given strings
int String::compare(const char *str1, const char *str2) const {
	return strcmp(str1, str2);
    
    while((*str1 && *str2) && (*str1++ == *str2++))
		;
	int diff = *str1 - *str2, retval = -1;
	if(diff == 0) {
		retval = 0;
	} else if(diff > 0) {
		retval = 1;
	}
    
	return retval;
}

// finds the position of the string "str2" inside the string "str"
int String::search(const char *str, const char *str2, PInt pos) const {
	int nLen = getlen(str2), retval = -1;
	const char *buffPtr = &str[pos];
	const char *startPtr = str2, *endPtr = startPtr;
	for(; *buffPtr != 0; ++buffPtr) {
		if(*buffPtr == *startPtr) {
			const char *buffPtrStart = buffPtr;
			int thisLen = 0;
			while(*buffPtr && (*buffPtr++ == *endPtr++)) {
				++thisLen;
			}
			if(thisLen == nLen || thisLen == 0) {
				retval = buffPtrStart - str;
				break;
			} else {
				endPtr = startPtr;
				--buffPtr;
			}
		}
	}
	return retval;
}

// converts a string of characters to uppercase
void String::uppercase(char *str) {
	char *buffPtr = str;
	for(; *buffPtr != 0; ++buffPtr) {
		if(*buffPtr >= 'a' && *buffPtr <= 'z') {
			*buffPtr -= 'a' - 'A';
		}
	}
}

// converts a string of characters to lowercase
void String::lowercase(char *str) {
	char *buffPtr = str;
	for(; *buffPtr != 0; ++buffPtr) {
		if(*buffPtr <= 'A' && *buffPtr >= 'Z') {
			*buffPtr += 'a' - 'A';
		}
	}
}

std::ostream& operator <<(std::ostream &os, const String &str) {
	PInt len = str.length();
	for(int i = 0; i < len; ++i) {
		os.put(str[i]);
	}
	return os;
}

std::istream& operator >>(std::istream &is, String &str) {
	char ch;
	while((ch = is.get()) && (ch != '\n' && ch != ' ')) {
		str += ch;
	}
	return is;
}

std::istream& getline(std::istream &is, String &str) {
	char ch;
	while((ch = is.get()) && ch != '\n') {
		str += ch;
	}
	return is;
}

// finds the position of the first character in the current string
// which is part of the set of characters "str"
int String::find_first_of(const String &str, PInt pos) const {
	PInt len = (*this).length();
	int _pos = -1;
	for(int i = pos; i < len; ++i) {
		if(str.find((*this)[i]) != -1) {
			_pos = i;
			break;
		}
	}
	return _pos;
}

// finds the position of the first character in the current string
// which is not part of the set of characters "str"
int String::find_first_not_of(const String &str, PInt pos) const {
	PInt len = (*this).length();
	int _pos = -1;
	for(int i = pos; i < len; ++i) {
		if(str.find((*this)[i]) == -1) {
			_pos = i;
			break;
		}
	}
	return _pos;
}


// finds the position of the last character in the current string
// which is part of the set of characters "str"
int String::find_last_of(const String &str, PInt pos) const {
	PInt len = (*this).length();
    pos = len - pos;
	int _pos = -1;
	for(int i = len - 1; i <= pos && i >= 0; --i) {
		if(str.find((*this)[i]) != -1) {
			_pos = i;
			break;
		}
	}
	return _pos;
}

// finds the position of the last character in the current string
// which is not part of the set of characters "str"
int String::find_last_not_of(const String &str, PInt pos) const {
	PInt len = (*this).length();
	int _pos = -1;
	for(int i = len - 1; i <= pos && i >= 0; --i) {
		if(str.find((*this)[i]) == -1) {
			_pos = i;
			break;
		}
	}
	return _pos;
}

void String::allocate_memory(PInt n) {
    szBuffer = new char[n];
}

void String::free_memory() {
	delete szBuffer;
	szBuffer = NULL;
}

String String::toString() {
    return *this;
}

String String::fromFormat(Prism::String format, ...) {
    va_list ap;
    va_start(ap, format); //Requires the last fixed parameter (to get the address)
    String str = String::fromFormatAP(format, ap);
    va_end(ap);
    return str;
}

String String::fromFormatAP(Prism::String format, va_list ap) {
    String str = "";
    
    for(int i = 0; i < format.length(); i++) {
        char ch = format[i];
        if (ch == '%') {
            if (format.length() - 1 == i) {
                str += "%";
                break;
            }
            i++;
            ch = format[i];
            switch (ch) {
                case '%':
                    str += "%";
                    break;
                case 'i':
                case 'd': {
                    int num = va_arg(ap, int);
                    char buffer[32];
                    sprintf(buffer, "%d", num);
                    str += buffer;
                } break;
                case 'u': {
                    unsigned int num = va_arg(ap, unsigned int);
                    char buffer[32];
                    sprintf(buffer, "%u", num);
                    str += buffer;
                } break;
                case 'f': {
                    float num = va_arg(ap, double);
                    str += num;
                } break;
                case 's': {
                    char * cstr = va_arg(ap, char *);
                    str += cstr;
                } break;
                case 'b': {
                    int b = va_arg(ap, int);
                    str += (b ? "TRUE" : "FALSE");
                } break;
                case '@': {
                    PObject* obj = va_arg(ap, PObject*);
                    str += obj->toString();
                } break;
                case '_': {
                    String* s = va_arg(ap, String*);
                    str += *s;
                } break;
                default: {
                    PRISM_LOGERROR("Format string contains unrecognized control character");
                    str += "[???]";
                } break;
            }
        } else {
            str += ch;
        }
    }
    
    return str;
}
